package com.example;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "/nasm")
public class Nasm {

  protected static final String URL = "nasm";
  private static final String 汇编码文件后缀 = ".asm";

  @RequestMapping(method = RequestMethod.GET)
  public String 输入(Model 模型) {
    模型.addAttribute("initcode",
        "section .data" + "\n" +
"    hello:     d1b 'Hello world!',10    ; 将d1b改为db后, 程序应该输出Hello world!" + "\n" +
"    helloLen:  equ $-hello             ; Length of the 'Hello world!' string" + "\n" +
"                                       ; (I'll explain soon)" + "\n" +
"" + "\n" +
"section .text" + "\n" +
"    global _start" + "\n" +
"" + "\n" +
"_start:" + "\n" +
"    mov eax,4            ; The system call for write (sys_write)" + "\n" +
"    mov ebx,1            ; File descriptor 1 - standard output" + "\n" +
"    mov ecx,hello        ; Put the offset of hello in ecx" + "\n" +
"    mov edx,helloLen     ; helloLen is a constant, so we don't need to say" + "\n" +
"                         ;  mov edx,[helloLen] to get it's actual value" + "\n" +
"    int 80h              ; Call the kernel" + "\n" +
"" + "\n" +
"    mov eax,1            ; The system call for exit (sys_exit)" + "\n" +
"    mov ebx,0            ; Exit with return code of 0 (no error)" + "\n" +
"    int 80h");
    return URL;
  }

  @RequestMapping(method = RequestMethod.POST)
  public String 编译运行(String 汇编码, Model 模型) {
    String 文件名;
    try {
      文件名 = 保存文件(汇编码);
    } catch (FileNotFoundException e1) {
      模型.addAttribute("compile", e1.getLocalizedMessage());

      模型.addAttribute("initcode", 汇编码);
      return URL;
    }
    try {
      结果 结果 = 编译(文件名);
      模型.addAttribute("compile", 结果);

      if (结果.错误.isEmpty()) {
        结果 = 链接(文件名);
        模型.addAttribute("ld", 结果);
      } else {

        模型.addAttribute("initcode", 汇编码);
        return URL;
      }

      if (结果.错误.isEmpty()) {
        结果 = 运行(文件名);
        模型.addAttribute("output", 结果.输出);
        模型.addAttribute("error", 结果.错误);
      } else {

        模型.addAttribute("initcode", 汇编码);
        return URL;
      }
    } catch (IOException e) {
      模型.addAttribute("genericerror", e.getLocalizedMessage());
      模型.addAttribute("initcode", 汇编码);
      return URL;
    }
    模型.addAttribute("initcode", 汇编码);

    return URL;
  }

  private String 保存文件(String 代码) throws FileNotFoundException {
    String 文件名 = System.currentTimeMillis() + "";
    try (PrintWriter 输出 = new PrintWriter(文件名 + 汇编码文件后缀)) {
      输出.println(代码);
    }
    return 文件名;
  }

  private class 结果 {
    String 错误;
    String 输出;

    public 结果(String 输出, String 错误) {
      this.输出 = 输出;
      this.错误 = 错误;
    }

    @Override
    public String toString() {
      return 错误.isEmpty() ? (输出.isEmpty() ? "无输出" : 输出) : 错误;
    }
  }

  private 结果 运行(String 文件名) throws IOException {
    return 运行程序("./" + 文件名);
  }

  private 结果 编译(String 文件名) throws IOException {
    return 运行程序("nasm", "-f", "elf64", 文件名 + 汇编码文件后缀);
  }

  private 结果 链接(String 文件名) throws IOException {
    return 运行程序("ld", "-s", "-o", 文件名, 文件名 + ".o");
  }

  private 结果 运行程序(String...命令行) throws IOException {
    Process 进程 = new ProcessBuilder(命令行).start();
    String 错误 = "";
      int 退出值 = 0;
      try {
        退出值 = 进程.waitFor();
      } catch (InterruptedException e) {
        错误 += e.getLocalizedMessage();
      }

    String 错误输出 = 读取字符(进程.getErrorStream());
    return new 结果(读取字符(进程.getInputStream()),
        错误输出.isEmpty() ? (退出值 != 0 ? "exit code: " + 退出值 + 错误 : 错误) : 错误输出);
  }

  private String 读取字符(InputStream 输入流) throws IOException {
    String 字符串 = "";
    InputStreamReader 输入流读取 = new InputStreamReader(输入流);
    BufferedReader 缓冲读取 = new BufferedReader(输入流读取);
    String 行;

    while ((行 = 缓冲读取.readLine()) != null) {
      字符串 += 行 + "\n";
    }
    return 字符串;
  }
}
